﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
#if ODIN_INSPECTOR
using Sirenix.OdinInspector;
#endif
// ReSharper disable All
namespace com.susi.util.toolkit
{
    /// <summary>
    /// 对里表字典进行一层包装。里表限定为<see cref="com.susi.util.toolkit.EventList{TItem}"/>
    /// <br>key在List在，不会出现key在list为null的情形</br>
    /// <br>适合key少，里表大的数据结构需求</br>
    /// </summary>
    public class EventBag<TKey, TItem> : IDictionary<TKey, EventList<TItem>>, IDisposaEventer, ICollection<KeyValuePair<TKey, EventList<TItem>>>, IEnumerable<KeyValuePair<TKey, EventList<TItem>>>, IEnumerable, IReadOnlyCollection<KeyValuePair<TKey, EventList<TItem>>>, IReadOnlyDictionary<TKey, EventList<TItem>>, ICollection, IDictionary, IDisposaEventer<EventBag<TKey, TItem>>
        where TItem : class
    {
#if ODIN_INSPECTOR
        [ShowInInspector]
#endif
        public readonly Dictionary<TKey, EventList<TItem>> InnerDic;

        public bool IsDisposed { get; private set; }
        public ICollection<TKey> Keys => InnerDic.Keys;

        public ICollection<EventList<TItem>> Values => InnerDic.Values;

        public int Count => InnerDic.Count;

        /// <summary>
        /// 没有意义
        /// </summary>
        public bool IsReadOnly => false;

        public bool IsSynchronized => false;

        public object SyncRoot => InnerDic;

        public bool IsFixedSize => false;

        ICollection IDictionary.Keys => InnerDic.Keys.ToArray();

        ICollection IDictionary.Values => InnerDic.Values.ToArray();

        IEnumerable<TKey> IReadOnlyDictionary<TKey, EventList<TItem>>.Keys => InnerDic.Keys;

        IEnumerable<EventList<TItem>> IReadOnlyDictionary<TKey, EventList<TItem>>.Values => InnerDic.Values;

        object IDictionary.this[object key]
        {
            get => InnerDic[(TKey)key];
            set
            {
                var lst = (IEnumerable<TItem>)value;
                var _key = (TKey)key;
                InnerDic[_key].AddRange(lst);
            }
        }
        /// <summary>
        /// 当set的时候，不会继承value的事件设置，只按差集变动Item
        /// </summary>
        public EventList<TItem> this[TKey key]
        {
            get
            {
                if (InnerDic.TryGetValue(key,out var lst))
                {
                    return lst;
                }
                return null;
            } 
            set
            {
                if(key==null)return;
                if (value == null || value.Count == 0)
                {
                    Remove(key);
                }
                else if (InnerDic.TryGetValue(key, out var evlst))
                {
                    var isChanged = false;
                    var remove = evlst.Except(value).ToList();
                    foreach (var item in remove)
                    {
                        if (!IsCrossRemovingFliter(item)) continue;
                        evlst.Remove(item);
                        onRemovedEvent?.Invoke(item);
                        isChanged = true;
                    }
                    var add = value.Except(evlst).ToList();
                    foreach (var item in add)
                    {
                        if (!IsCrossAddingFliter(item)) continue;
                        evlst.Add(item);
                        onAddedEvent?.Invoke(item);
                        isChanged = true;
                    }
                    if (isChanged) onChangedEvent?.Invoke();
                }
                else
                {
                    InnerDic.Add(key, CreatEventList(value));
                    foreach (var item in value)
                    {
                        onAddedEvent?.Invoke(item);
                    }
                    onChangedEvent?.Invoke();
                }
            }
        }
#pragma warning disable IDE1006 // 命名样式
        public event Func<TItem, bool> AddingFliter;
        /// <summary>
        /// 当本对象释放的时候，RemovingFliter会被无视
        /// </summary>
        public event Func<TItem, bool> RemovingFliter;
#pragma warning restore IDE1006 // 命名样式
        public event Action<TItem> onAddedEvent;
        public event Action<TItem> onRemovedEvent;
        public event Action onChangedEvent;
        public event Action onDisposingEvent;
        public event Action<EventBag<TKey, TItem>> onDisposingTEvent;

        public EventBag()
        {
            InnerDic = new Dictionary<TKey, EventList<TItem>>();
        }

        bool IsCrossAddingFliter(TItem item)
        {
            if (AddingFliter == null) return true;
            foreach (Func<TItem, bool> deg in AddingFliter.GetInvocationList())
            {
                if (!deg(item)) return false;
            }
            return true;
        }
        bool IsCrossRemovingFliter(TItem item)
        {
            if (RemovingFliter == null) return true;
            foreach (Func<TItem, bool> deg in RemovingFliter.GetInvocationList())
            {
                if (!deg(item)) return false;
            }
            return true;
        }

        void _onAdd(TItem item) { onAddedEvent?.Invoke(item); }
        void _onRemove(TItem item) { onRemovedEvent?.Invoke(item); }
        void _onChange() => onChangedEvent?.Invoke();
        void _onDis(EventList<TItem> ts)
        {
            RemoveControl(ts);
        }
        EventList<TItem> AddControl(EventList<TItem> ts)
        {
            ts = RemoveControl(ts);
            ts.onAddingFliter += IsCrossAddingFliter;
            ts.onRemovingFliterEvent += IsCrossRemovingFliter;
            ts.onAddedEvent += _onAdd;
            ts.onRemovedEvent += _onRemove;
            ts.onChangedEvent += _onChange;
            ts.onDisposingTEvent += _onDis;
            return ts;
        }
        EventList<TItem> RemoveControl(EventList<TItem> ts)
        {
            ts.onDisposingTEvent -= _onDis;
            ts.onAddingFliter -= IsCrossAddingFliter;
            ts.onRemovingFliterEvent -= IsCrossRemovingFliter;
            ts.onAddedEvent -= _onAdd;
            ts.onRemovedEvent -= _onRemove;
            ts.onChangedEvent -= _onChange;
            return ts;
        }
        /// <summary>
        /// 等同<see cref="Add(TKey, IEnumerable{TItem})"/>
        /// </summary>
        public void Add(TKey key, EventList<TItem> value)
        {
            Add(key, (IEnumerable<TItem>)value);
        }
        /// <summary>
        /// 在该key增加value中的元素，不会引用value自身
        /// </summary>
        public void Add(TKey key, IEnumerable<TItem> value)
        {
            if (value == null) return;
            if (!InnerDic.TryGetValue(key, out var evlst))
            {
                var add = value.Except(evlst).ToArray();
                evlst.AddRange(add);
            }
            else
            {
                var evlst1 = CreatEventList(value);
                InnerDic.Add(key, evlst);
                evlst.AddRange(value);
            }
        }
        /// <summary>
        /// 会新建key
        /// </summary>
        public void Add(TKey key, TItem item)
        {
            AddToInnerList(key, item);
            //if (InnerDic.TryGetValue(key,out var lst))
            //{
            //    lst.Add(item);
            //}
        }
        /// <summary>
        /// 会新建key
        /// </summary>
        public void AddToInnerList(TKey key, TItem item)
        {
            if (!InnerDic.ContainsKey(key))
            {
                InnerDic.Add(key, CreatEventList());
            }
            InnerDic[key].Add(item);
        }
        /// <summary>
        /// 创建一个已经添加监听器的<see cref="EventList{TItem}"/>
        /// </summary>
        private EventList<TItem> CreatEventList(IEnumerable<TItem> items)
        {
            return AddControl(new EventList<TItem>(new List<TItem>(items)));
        }
        /// <summary>
        /// 创建一个已经添加监听器的<see cref="EventList{TItem}"/>
        /// </summary>
        private EventList<TItem> CreatEventList()
        {
            return AddControl(new EventList<TItem>(new List<TItem>()));
        }

        /// <summary>
        /// 没有移除任何元素返回false，否则返回true;只会移除里表的可移除项目;
        /// <para><see langword="依然会保留key"/></para>
        /// </summary>
        public bool Remove(TKey key)
        {
            if (InnerDic.TryGetValue(key, out var lst))
            {
                var copy = lst.ToArray();
                return lst.RemoveRange(copy);
            }
            return false;
        }

        /// <summary>
        /// 移除第一个匹配元素
        /// </summary>
#if ODIN_INSPECTOR
        [LabelText("删除里表的物体"), Button("常用")]
#endif
        public bool RemoveValue(TItem item)
        {
            var res = InnerDic.FirstOrDefault(v => v.Value.Contains(item));
            if (res.Value == null) return false;
            return res.Value.Remove(item);
        }
        /// <summary>
        /// 不会删除空表
        /// </summary>
        public bool RemoveFromInnerList(TKey key, TItem item)
        {
            if (InnerDic.TryGetValue(key, out var lst))
            {
                return lst.Remove(item);
            }
            return false;
        }
        /// <summary>
        /// 会删除空表
        /// </summary>
        public bool RemoveFromInnerListAndTrim(TKey key, TItem item)
        {
            if (this.TryGetValue(key, out var lst))
            {
                var res = lst.Remove(item);
                if (lst.Count == 0)
                {
                    lst.Dispose();
                    this.Remove(key);
                }
                return res;
            }
            else return false;
        }

        public bool TryGetValue(TKey key, out EventList<TItem> value)
        {
            return InnerDic.TryGetValue(key, out value);
        }
        /// <summary>
        /// 等同<see cref="Add(TKey, IEnumerable{TItem})"/>
        /// </summary>
        public void Add(KeyValuePair<TKey, EventList<TItem>> item)
        {
            Add(item.Key, item.Value);
        }
        /// <summary>
        /// 按kvp添加一组物体，会创建新key，会同时触发<see cref="onAddedEvent"/>和<see cref="onChangedEvent"/>
        /// </summary>
        public void AddRange(IEnumerable<KeyValuePair<TKey, TItem>> enumerable)
        {
            var dic = from kvp in enumerable
                      group kvp.Value by kvp.Key into g
                      select g;
            foreach (var item in dic)
            {
                AddRange(item.Key, item);
            }
        }
        /// <summary>
        /// 按key添加一组物体，会创建新key，会同时触发<see cref="onAddedEvent"/>和最多1次<see cref="onChangedEvent"/>
        /// </summary>
        public void AddRange(TKey key, IEnumerable<TItem> items)
        {
            if (!InnerDic.ContainsKey(key)) InnerDic.Add(key, CreatEventList());
            InnerDic[key].AddRange(items);
        }

        /// <summary>
        /// 无视Fliter进行清空,会删除key, 会调用<see cref="EventList{T}.Dispose"/>,会触发Key次数的<see cref="onChangedEvent"/>
        /// <br>不会改变事件设置</br>
        /// </summary>
        public void Clear()
        {
            foreach (var va in InnerDic.Values)
            {
                va.Dispose();
            }
            InnerDic.Clear();
        }
        /// <summary>
        /// 按Fliter进行清空,会删除空表key, 对空表会调用<see cref="EventList{T}.Dispose"/>
        /// <br>不会改变事件设置</br>
        /// </summary>
#if ODIN_INSPECTOR
        [LabelText("清空里表的可移除物体"), Button("常用")]
#endif
        public void ClearWithFliter()
        {
            foreach (var va in InnerDic.ToArray())
            {
                va.Value.ClearWithFliter();
                if (va.Value.Count == 0)
                {
                    va.Value.Dispose();
                    InnerDic.Remove(va.Key);
                }
            }
        }

        public bool Contains(TItem item)
        {
            foreach (var kv in InnerDic)
            {
                if (kv.Value.Contains(item)) return true;
            }
            return false;
        }

        public bool Contains(KeyValuePair<TKey, EventList<TItem>> item)
        {
            return InnerDic.Contains(item);
        }

        public void CopyTo(KeyValuePair<TKey, EventList<TItem>>[] array, int arrayIndex)
        {
            if (array == null) throw new ArgumentNullException(nameof(array));
            if (arrayIndex >= array.Length) throw new ArgumentOutOfRangeException(nameof(arrayIndex));
            var itor = InnerDic.GetEnumerator();
            for (; arrayIndex < array.Length && itor.MoveNext(); arrayIndex++)
            {
                array[arrayIndex] = itor.Current;
            }
        }

        public bool Remove(KeyValuePair<TKey, EventList<TItem>> item)
        {
            foreach (var kv in InnerDic)
            {
                if (kv.Key.Equals(item.Key) && kv.Value == item.Value)
                {
                    InnerDic.Remove(item.Key);
                    return true;
                }
            }
            return false;
        }

        public IEnumerator<KeyValuePair<TKey, EventList<TItem>>> GetEnumerator()
        {
            return InnerDic.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return InnerDic.GetEnumerator();
        }
        /// <summary>
        /// 会调用<see cref="Clear"/>
        /// </summary>
#if ODIN_INSPECTOR
        [LabelText("释放"), Button("常用")]
#endif
        public void Dispose()
        {
            if (IsDisposed) return;
            IsDisposed = true;
            onDisposingEvent?.Invoke();
            onDisposingTEvent?.Invoke(this);
            onDisposingEvent = null;
            onDisposingTEvent = null;
            Clear();
            AddingFliter = null;
            RemovingFliter = null;
            onAddedEvent = null;
            onRemovedEvent = null;
            onChangedEvent = null;
        }

        public void CopyTo(Array array, int index)
        {
            if (array == null) throw new ArgumentNullException(nameof(array));
            if (index > array.Length) throw new ArgumentOutOfRangeException(nameof(index));
            var itor = InnerDic.GetEnumerator();
            for (; index < array.Length && itor.MoveNext(); index++)
            {
                array.SetValue(itor.Current, index);
            }
        }

        void IDictionary.Add(object key, object value)
        {
            Add((TKey)key, (EventList<TItem>)value);
        }

        bool IDictionary.Contains(object key)
        {
            return ((IDictionary)InnerDic).Contains(key);
        }

        IDictionaryEnumerator IDictionary.GetEnumerator()
        {
            return ((IDictionary)InnerDic).GetEnumerator();
        }

        void IDictionary.Remove(object key)
        {
            Remove((KeyValuePair<TKey, EventList<TItem>>)key);
        }

        public bool ContainsKey(TKey key)
        {
            return InnerDic.ContainsKey(key);
        }
    }
}
